package hikari.utils;

import com.gooddies.wiring.Wiring;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.exception.CancelActionException;
import hikari.exception.ValidationException;
import hikari.gui.MainWindow;
import hikari.gui.panels.HierarchyTree;
import hikari.managers.Manager3D;
import hikari.objects3d.Basic3dObject;
import hikari.objects3d.light.Light3dObject;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JColorChooser;
import javax.swing.JOptionPane;
import javax.swing.tree.DefaultMutableTreeNode;
import org.apache.commons.io.FileUtils;
import org.swig.jirr.ISceneNode;
import org.swig.jirr.ITexture;
import org.swig.jirr.SColor;
import org.swig.jirr.vector2di;

/**
 * Created with IntelliJ IDEA. User: Dmitry Date: 24.11.12 Time: 22:11
 */
@WiringComponent(singleton = true, lazy = true)
public class Utils {

    public void setModified() {
        Wiring.getComponent(MainWindow.class).setModified();
    }

    public void clearModified() {
        Wiring.getComponent(MainWindow.class).setClearModified();
    }

    public String getWorkDirectory() {
        try {
            return new File(".").getCanonicalPath();
        } catch (IOException ex) {
            Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public int random(int max) {
        return (int) (Math.random() * max);
    }

    public int random(int min, int max) {
        return (int) (Math.random() * (max - min)) + min;
    }

    public double random(double min, double max) {
        return (Math.random() * (max - min)) + min;
    }

    public double random(double max) {
        return Math.random() * max;
    }

    public String combinePaths(String path, String path2) {
        if (path == null) {
            return path2 == null ? "" : path2;
        }
        path = path.replace('/', '\\');
        path2 = path2.replace('/', '\\');
        if (path.endsWith("\\")) {
            path = path.substring(0, path.length() - 1);
        }
        if (path2.startsWith("\\")) {
            path2 = path2.substring(1);
        }

        return path + "\\" + path2;
    }

    public String combinePath(String chunk, String chunk2, String... chunks) {
        String path = combinePaths(chunk, chunk2);
        for (String c : chunks) {
            path = combinePaths(path, c);
        }
        return path;
    }

    public String makePathRelative(String path, String base) {
        path = path.replace('/', '\\').trim();
        base = base.replace('/', '\\').trim();
        if (path.toLowerCase().startsWith(base.toLowerCase())) {
            return path.substring(base.length());
        }
        return path;
    }

    public String formatPathWithNewExtension(String filePath, String newExtension) {
        if (!newExtension.startsWith(".")) {
            newExtension = "3" + newExtension;
        }

        int index = filePath.lastIndexOf('.');
        if (index == -1) {
            return filePath + newExtension;
        }

        return filePath.substring(0, index) + newExtension;
    }

    public ImageIcon loadIconFromResource(String path) {
        String newPath = "icons/" + path;
        BufferedImage image = loadImageFromResources(newPath);
        if (image == null) {
            throw new RuntimeException("Cannot load icon ");
        }
        return new ImageIcon(image);
    }

    public BufferedImage loadImageFromResources(String path) {
        try {
            BufferedImage image;
            try (InputStream stream = Utils.class.getResourceAsStream("/hikari/resource/" + path)) {
                if (stream == null) {
                    errorMessage("Image by path " + path + " is not found");
                }
                image = ImageIO.read(stream);
            }
            return image;
        } catch (IOException ex) {
            Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public Basic3dObject ISceneNodeToBasic3dObject(ISceneNode node) {
        if (node == null) {
            return null;
        }

        DefaultMutableTreeNode treeNode = Wiring.getComponent(HierarchyTree.class).searchNode(node);
        if (treeNode == null) {
            return null;
        }

        return (Basic3dObject) treeNode.getUserObject();
    }

    public String endWithFileSeparator(String path) {
        if (!path.endsWith("" + File.separatorChar)) {
            return path + File.separatorChar;
        }
        return path;
    }

    public String getFileNameWithoutExtension(String path) {
        if (path == null || path.trim().equals("")) {
            return "";
        }
        path = path.replace('\\', '/');
        String name;
        int index = path.lastIndexOf('/');
        if (index == -1) {
            name = path;
        } else {
            name = path.substring(index + 1);
        }

        index = name.lastIndexOf('.');
        if (index == -1) {
            return name;
        } else {
            return name.substring(0, index);
        }
    }

    public String getFilePath(String path) {
        path = path.replace('\\', '/');
        String name;
        int index = path.lastIndexOf('/');
        if (index == -1) {
            name = "";
        } else {
            name = path.substring(0, index + 1);
        }
        return name;
    }

    public String getFileExtension(String path) {
        int index = path.lastIndexOf('.');
        if (index == -1) {
            return "";
        }
        return path.substring(index + 1).toLowerCase();
    }

    public SColor colorPicker(Component parent, SColor initialColor) {
        Color result;
        if (initialColor != null) {
            result = JColorChooser.showDialog(parent, "Please select color", initialColor.getJavaColor());
        } else {
            result = JColorChooser.showDialog(parent, "Please select color", Color.BLACK);
        }
        return new SColor(result);
    }

    public String prompt(Component parent, String title) {
        return JOptionPane.showInputDialog(parent, "My message", title, JOptionPane.INFORMATION_MESSAGE);
    }

    public String prompt(Component parent, String title, String message) {
        return JOptionPane.showInputDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
    }

    public Boolean confirm(Window parent, String caption, String message) {
        int res = JOptionPane.showConfirmDialog(parent, message, caption, JOptionPane.YES_NO_CANCEL_OPTION);
        if (res == JOptionPane.YES_OPTION) {
            return true;
        }
        if (res == JOptionPane.NO_OPTION) {
            return false;
        }

        return null;
    }

    public String prompt(Component parent, String title, String message, String allowedChars, String errorMessage) {
        String initial = "";
        while (true) {
            String result = JOptionPane.showInputDialog(null, message, initial);
            if (result == null) {
                return null;
            }
            Pattern p = Pattern.compile("[" + allowedChars + "]*");
            Matcher m = p.matcher(result);
            if (m.matches()) {
                return result;
            }

            initial = result;
            message = errorMessage;
        }
    }

    public void errorMessage(Component parent, String message) {
        JOptionPane.showMessageDialog(parent, message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void errorMessage(String message) {
        JOptionPane.showMessageDialog(Wiring.getComponent(MainWindow.class), message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void errorMessage(Throwable throwable) {
        boolean shouldPrintstackTrace = true;
        String message = throwable.getMessage();
        if (throwable instanceof CancelActionException) {
            return;
        }

        if (throwable instanceof ValidationException) {
            shouldPrintstackTrace = false;
        }

        if (message == null || message.isEmpty()) {
            message = throwable.getClass().getSimpleName();
        }

        if (shouldPrintstackTrace) {
            throwable.printStackTrace();
        }

        JOptionPane.showMessageDialog(Wiring.getComponent(MainWindow.class), message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void showMessage(String message) {
        JOptionPane.showMessageDialog(Wiring.getComponent(MainWindow.class), message, "Info", JOptionPane.INFORMATION_MESSAGE);
    }

    public String readStringFromResource(String path) {
        InputStream stream = Utils.class.getResourceAsStream("/hikari/resource/" + path);
        if (stream == null) {
            throw new RuntimeException("Resource " + path + " not found");
        }
        StringBuilder sb;
        try (Scanner scanner = new Scanner(stream, "UTF8")) {
            sb = new StringBuilder(1000);
            boolean first = true;
            while (scanner.hasNextLine()) {
                if (first == false) {
                    sb.append('\n');
                }

                sb.append(scanner.nextLine());
                first = false;
            }
        }

        return sb.toString();
    }

    public void logError(String message) {
        System.err.println(message);
    }

    public void logError(Throwable err) {
        err.printStackTrace();
    }

    public ITexture loadTextureFromResource(String resourcesPath) {
        try {
            String extension = "." + getFileExtension(resourcesPath);
            File tempTexture = File.createTempFile("texture", extension);
            String path = "/hikari/resource/" + resourcesPath;
            InputStream stream = Light3dObject.class.getResourceAsStream(path);
            if (stream == null) {
                throw new RuntimeException("Cannot find texture by path " + path);
            }
            FileUtils.copyInputStreamToFile(stream, tempTexture);
            ITexture texture = ((Manager3D) Wiring.getComponent(Manager3D.class)).getVideoDriver().getTexture(tempTexture.getAbsolutePath());
            return texture;
        } catch (IOException ex) {
            Logger.getLogger(Light3dObject.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public void start(String path) {
        try {
            Desktop.getDesktop().open(new File(path));
        } catch (IOException ex) {
            Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int[] extendArray(int[] original, int[] toAppend) {
        int[] newarray = new int[original.length + toAppend.length];
        int index = 0;
        for (; index < original.length; index++) {
            newarray[index] = original[index];
        }

        for (int i = 0; i < toAppend.length; i++, index++) {
            newarray[index] = toAppend[i];
        }

        return newarray;
    }

    @SuppressWarnings("unchecked")
    public <T> T[] deleteArrayElement(T[] array, int indexToDelete) {
        if (array.length == 0) {
            return array;
        }
        Class clazz = array.getClass().getComponentType();
        T[] newArray = (T[]) Array.newInstance(clazz, array.length - 1);
        int index = 0;
        for (int i = 0; i < array.length; i++) {
            if (i == indexToDelete) {
                continue;
            }
            newArray[index] = array[i];
            index++;
        }
        return newArray;
    }

    public int[] deleteArrayElement(int[] array, int indexToDelete) {
        if (array.length == 0) {
            return array;
        }
        int[] newArray = new int[array.length - 1];
        int index = 0;
        for (int i = 0; i < array.length; i++) {
            if (i == indexToDelete) {
                continue;
            }
            newArray[index] = array[i];
            index++;
        }
        return newArray;
    }

    public static boolean lineHitTest(vector2di l1Start, vector2di l1End, vector2di l2Start, vector2di l2End) {
        int l1x1 = l1Start.getX();
        int l1y1 = l1Start.getY();
        int l1x2 = l1End.getX();
        int l1y2 = l1End.getY();

        int l2x1 = l2Start.getX();
        int l2y1 = l2Start.getY();
        int l2x2 = l2End.getX();
        int l2y2 = l2End.getY();

        int v1, v2, v3, v4;
        v1 = (l2x2 - l2x1) * (l1y1 - l2y1) - (l2y2 - l2y1) * (l1x1 - l2x1);
        v2 = (l2x2 - l2x1) * (l1y2 - l2y1) - (l2y2 - l2y1) * (l1x2 - l2x1);
        v3 = (l1x2 - l1x1) * (l2y1 - l1y1) - (l1y2 - l1y1) * (l2x1 - l1x1);
        v4 = (l1x2 - l1x1) * (l2y2 - l1y1) - (l1y2 - l1y1) * (l2x2 - l1x1);
        boolean result = ((v1 * v2) < 0) && ((v3 * v4) < 0);
        return result == true;
    }

    public double length(float x1, float y1, float z1, float x2, float y2, float z2) {
        return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1));
    }
}
